use std::sync::Arc;

use lazy_static::lazy_static;
use redb::TableDefinition;
use ruma::{DeviceId, TransactionId, UserId};

use crate::{Result, DATABASE};

const USERDEVICETXNID_RESPONSE_DEFINITION: TableDefinition<&[u8], &[u8]> =
    TableDefinition::new("user_device_txn_id_response");

lazy_static! {
    pub static ref SERVICE: Arc<Service> = Arc::new(Service::new());
}

pub struct Service {}

impl Service {
    pub fn new() -> Self {
        Self {}
    }
    pub fn add_txnid(
        &self,
        user_id: &UserId,
        device_id: Option<&DeviceId>,
        txn_id: &TransactionId,
        data: &[u8],
    ) -> Result<()> {
        let mut key = user_id.as_bytes().to_vec();
        key.push(0xFF);
        key.extend_from_slice(device_id.map(DeviceId::as_bytes).unwrap_or_default());
        key.push(0xFF);
        key.extend_from_slice(txn_id.as_bytes());

        let db = DATABASE.lock().unwrap();
        let write_ctx = db.begin_write().map_err(redb::Error::from)?;
        {
            let mut txnid_table = write_ctx
                .open_table(USERDEVICETXNID_RESPONSE_DEFINITION)
                .map_err(redb::Error::from)?;
            txnid_table
                .insert(key.as_slice(), data)
                .map_err(redb::Error::from)?;
        }
        write_ctx.commit().map_err(redb::Error::from)?;
        Ok(())
    }

    pub fn existing_txnid(
        &self,
        user_id: &UserId,
        device_id: Option<&DeviceId>,
        txn_id: &TransactionId,
    ) -> Result<Option<Vec<u8>>> {
        let mut key = user_id.as_bytes().to_vec();
        key.push(0xFF);
        key.extend_from_slice(device_id.map(DeviceId::as_bytes).unwrap_or_default());
        key.push(0xFF);
        key.extend_from_slice(txn_id.as_bytes());

        let db = DATABASE.lock().unwrap();
        let read_ctx = db.begin_read().map_err(redb::Error::from)?;
        let txnid_table = read_ctx
            .open_table(USERDEVICETXNID_RESPONSE_DEFINITION)
            .map_err(redb::Error::from)?;
        let res = txnid_table.get(key.as_slice()).map_err(redb::Error::from)?;

        Ok(res.map(|x| x.value().to_owned()))
    }
}
